﻿using System;
using System.Linq;
using System.Text;

namespace Coogle.Core
{
    public class InvalidContainerCodeException : FormatException
    {
        public InvalidContainerCodeException()
            : base("Invalid format.")
        {
        }
    }

    public class ContainerCode
    {
        private const int CONTAINER_CODE_LENGHT = 11;

        private readonly String m_sign;
        private readonly Int32 m_number;
        private readonly Int32 m_check;

        public ContainerCode(String sign, Int32 number, Int32 check)
        {
            m_sign = sign;
            m_check = check;
            m_number = number;
        }

        public override Boolean Equals(object obj)
        {
            if (obj == null) return false;
            var cc = obj as ContainerCode;
            if (cc == null) return false;

            return (m_sign.Equals(cc.m_sign) &&
                    m_number.Equals(cc.m_number) &&
                    m_check.Equals(cc.m_check));
        }

        public override Int32 GetHashCode()
        {
            return m_sign.GetHashCode() ^ m_number.GetHashCode() ^ m_check.GetHashCode();
        }

        public override String ToString()
        {
            return Sign + " " + Number + " " + Check;
        }

        public static ContainerCode Parse(string value)
        {
            var clearedValue = RemoveSeparators(value, ' ', '-', '.', '/', '\\');
            if (clearedValue.Length != CONTAINER_CODE_LENGHT)
                throw new InvalidContainerCodeException();

            String sign = clearedValue.Substring(0, 4).ToUpper();
            if (IsAllLetter(sign) == false)
                throw new InvalidContainerCodeException();

            Int32 number;
            if (Int32.TryParse(clearedValue.Substring(4, 6), out number) == false)
                throw new InvalidContainerCodeException();

            Int32 check;
            if (Int32.TryParse(clearedValue.Substring(10, 1), out check) == false)
                throw new InvalidContainerCodeException();

            return new ContainerCode(sign, number, check);
        }

        private static bool IsAllLetter(String value)
        {
            var chars = value.ToCharArray();
            return chars.All(c => Char.IsLetter(c));
        }

        private static String RemoveSeparators(String value, params Char[] separators)
        {
            var chars = value.ToCharArray();
            var sb = new StringBuilder(CONTAINER_CODE_LENGHT);
            for (var i = 0; i < chars.Length; i++)
            {
                if (Array.IndexOf(separators, chars[i]) == -1)
                    sb.Append(chars[i]);
            }
            return sb.ToString();
        }

        public String Sign
        {
            get
            {
                return m_sign;
            }
        }

        public Int32 Number
        {
            get
            {
                return m_number;
            }
        }

        public Int32 Check
        {
            get
            {
                return m_check;
            }
        }
    }
}
